<!doctype html>
<html lang="en">
<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"
          integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">

    <title>Vizit</title>
    <script src="../node_modules/jquery/dist/jquery.js"></script>
    <script src="../node_modules/@svgdotjs/svg.js/dist/svg.js"></script>
    <script src="../node_modules/@svgdotjs/svg.draggable.js/dist/svg.draggable.js"></script>
    <script src="scripts/snap.svg.js"></script>
    <script src="http://svg.dabbles.info/animate-object-path.js"></script>
    <!--    <script src="//cdnjs.cloudflare.com/ajax/libs/velocity/2.0.6/velocity.min.js"></script>-->

</head>
<style>
    .dot {
        stroke: #b5580d;
        stroke-width: 5;
        /*stroke-dasharray: 471;*/
        /*stroke-dashoffset: 0;*/
    }
    .text {
        font-size: 20px;
        font-weight: bold;
        width: 50px;
    }
    .node_timer {
        stroke-width: 8;
        stroke: blue;
        stroke-dasharray: 315, 315;
        stroke-dashoffset: 315;
        opacity: 0.5;
        fill: none
    }
</style>
<body background="#D8FFDC">
<div id="main-content" class="container">

    <div class="row">
        <div class="col-md-12">
            <div id="debug_alert0" class="alert alert-success text-center" style="font-size: 20px;font-weight: bold;" role="alert">
                线程池运行原理 动画演示
            </div>
        </div>
    </div>

    <div class="row">
        <div class="col-md-12">
            <div id="debug_alert" class="alert alert-success text-center" style="font-size: 20px;font-weight: bold;" role="alert">
                线程池参数：</br>ThreadPoolExecutor executor  = new ThreadPoolExecutor(
                3, 6, 60, TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(7), threadFactory, rejectedExecutionHandler)
            </div>
        </div>
    </div>

</div>
<div class="container-fluid">

    <div class="row">
        <div id='paper' class="col-md-8">
            <!-- SVG paper -->
        </div>
        <div id='threadStatus' class="col-md-8" style="display: none;">
            <img src="data/threadpool/thread.jpg">
            <!-- SVG paper -->
        </div>

                <div id='pannel' class="col-md-4 bg-light">
                    <div class="container">
                        <div class="row">
                            <div class="card">
                                <div class="card-body">
                                    <h5 class="card-title">问题</h5>
                                    <ul class="list-group list-group-flush">
                                        <li class="list-group-item">线程池有哪几个参数，分表代表什么意思？</li>
                                        <li class="list-group-item">线程池底层原理？</li>
                                        <li class="list-group-item">线程池第三个参数有什么作用？</li>
                                        <li class="list-group-item">多创建的线程怎么回收？</li>
                                        <li class="list-group-item">核心线程能回收吗？</li>
                                        <li class="list-group-item">线程池怎么保证空闲线程一直运行的？</li>
                                        <li class="list-group-item">线程有哪几种状态？</li>
                                        <li class="list-group-item">线程池worker为什么继承AbstractQueuedSynchronizer</li>
                                        <li class="list-group-item">线程池拒绝策略有哪些，分别代表什么意思？</li>
                                        <h5 class="card-title">拒绝策略</h5>
                                        <ul class="list-group list-group-flush">
                                            <li class="list-group-item">AbortPolicy 直接抛出异常 RejectedExecutionExceptio</li>
                                            <li class="list-group-item">DiscardPolicy-当新任务被提交后直接被丢弃掉，也不会给你任何的通知</li>
                                            <li class="list-group-item">DiscardOldestPolicy - 丢弃任务队列中的头结点，通常是存活时间最长的任</li>
                                            <li class="list-group-item">CallerRunsPolicy - 谁提交任务，谁就负责执行任务</li>
                                        </ul>

                                    </ul>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
    </div>
    <div class="row">
        <div id='actions' class="col-md-12 bg-light">
            <div class="container">
                <div class="row">
                    <div class="card">
                        <div class="card-body">
                            <ul class="list-group list-group-horizontal-md">
                                <li class="list-group-item"><a id="next" href="#" class="card-link">Next
                                <li class="list-group-item"><a id="image" href="#" class="card-link">threadStatusImage
                                </a></li>

                            </ul>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
<script>
    // 定义svg对象, 在body里面添加一个svg
    let svg = SVG().addTo('#paper').attr({width: 1200, height: 750, id: 'svg'});
    svg.rect(1200, 750).fill("#E3EDCD");
    var snap = Snap("#svg");
    // marker: arrow head
    let marker = svg.marker(13, 13, function (add) {
        add.path('M2,2 L2,11 L10,6 L2,2').fill('blue').stroke({width: 1, color: 'blue'})
    }).attr({"refX": "2", "refY": "6", "orient": "auto"});
    // Set the global configs to synchronous
    $.ajaxSetup({
        async: false
    });
    let version = Date.now();

    function createNode(container, data) {
        // 用一个group把节点边框和文本放在一起，这样可以利用group的transform来定义
        // 整个group里面所有节点的坐标位置
        let translateX = data.group.transform.translateX;
        let translateY = data.group.transform.translateY;

        let label = container.text(data.label.text);
        // let label = container.text('');

        let width = data.group.width == null ? (data.label.text.length * 8 + 10) : parseInt(data.group.width);
        let height = data.group.height == null ? 28 : parseInt(data.group.height);
        let group = container.group()
            .transform({translateX: translateX, translateY: translateY})
            .addClass('leaf')
            .id(data.group.id)
            .data('leftTopX', translateX)
            .data('leftTopY', translateY)
            .data('width', width)
            .data('height', height)
        ;
        // TODO style 的信息暂时hardcode, 后面会用程序控制动态变化的效果。
        let style = {fill: 'none', stroke: 'blue'};
        let attributes = {...style, ...data.shape.attributes};
        // 动态的调用函数，可以支持绘制各种图形，比如:rect, ellipse
        let rect = container[data.shape.type](width, height).attr(attributes);
        group.add(rect);

        label.attr(data.label.attributes);
        group.add(label);
        return group.draggable(); // 支持拖拽
    }

    function buildPathId(start, end) {
        return `${start}_${end}`;
    }

    function show(...ids) {
        for (let id of ids) {
            snap.select(`#${id}`).attr({display: ''});
        }
    }

    function hide(...ids) {
        for (let id of ids) {
            console.log(`hide #${id}`);
            snap.select(`#${id}`).attr({display: 'none'});
        }
    }

    function fail(...ids) {
        for (let id of ids) {
            console.log(`fail #${id}`);
            snap.select(`#${id} rect`).attr({"stroke-dasharray": "5,5", stroke: 'red'});
        }
    }

    function restore(...ids) {
        for (let id of ids) {
            console.log(`fail #${id}`);
            snap.select(`#${id} rect`).attr({"stroke-dasharray": "", stroke: 'blue'});
        }
    }

    function buildPath(start, endList, i, path) {
        let startNode = svg.findOne(`#${start}`);
        let endNode = svg.findOne(`#${endList[i]}`);
        let startX = startNode.data('leftTopX') + startNode.data('width') + path.startOffsetX;
        let startY = startNode.data('leftTopY') + startNode.data('height') + path.startOffsetY;
        let endX = endNode.data('leftTopX') + path.endOffsetX;
        let endY = endNode.data('leftTopY') + path.endOffsetY;
        let data = `M ${startX}, ${startY} ${endX}, ${endY}`;
        if (path.curve != null) {
            data = `M ${startX}, ${startY} ${path.curve} ${endX}, ${endY}`;
        }
        return {startX, startY, endX, endY, data};
    }

    function animate(pathId,callback=null) {
        let move = snap.select(`#${pathId}`);
        let points = $(`#${pathId}`).data('points');
        let circle = snap.circle(points[0], points[1], 10);
        circle.attr({
            fill: 'green',
            id: 'circle-' + pathId,
            stroke: "green",
            strokeWidth: 2
        });
        circle.drawAtPath(move, 1000, {
            callback: function () {
                circle.remove();
                if(callback!=null){
                    callback();
                }
            }
        });

    }

    function createPath(container, path, marker,offset=0) {
        let start = path.start;
        let endList = path.end;
        for (let i = 0; i < endList.length; i++) {
            let {startX, startY, endX, endY, data} = buildPath(start, endList, i, path);
            let id = buildPathId(start, endList[i]);
            if(offset!==0){
                id=id+offset;
            }
            let pathId = `${start}_${endList[i]}`;
            let group = container.group().id(`data-flow-${pathId}`).addClass('leaf path');
            group.path(data).id(id)
                .fill('none')
                .attr(path.attr)
                .marker('end', marker)
                .data('points', [startX, startY, endX, endY])
                .data('d', data);
        }
    }

    function createSinglePath(container, path) {
        let clonePath = {...path};
        clonePath.end = [path.end];
        createPath(container, clonePath, marker);
    }

    function createMatrixGroup(container, data) {
        let group = container.group().id(data.group.name).addClass('matrix-group');
        group.rect(
            data.group.border.width,
            data.group.border.height)
            .attr(
                {
                    ...{
                        x: data.group.transform.translateX,
                        y: data.group.transform.translateY - 5
                    },
                    ...data.group.border.attr
                });
        group.text(data.title.text).attr({
            x: data.group.transform.translateX + data.title.offset.x,
            y: data.group.transform.translateY + data.title.offset.y
        });
        return group.addClass('leaf text');
    }

    // a matrix is a group of nodes replicated from data
    function createMatrix(container, data) {
        let group = createMatrixGroup(container, data);

        let matrix = data.shape.matrix;
        if (matrix != null) {
            for (let i = 0; i < matrix.row.count; i++) {
                let row = group.group().id(data.group.id.replace("{row}", i).replace("{column}", 'x'));
                for (let j = 0; j < matrix.column.count; j++) {
                    let nodeReplica = JSON.parse(JSON.stringify(data));
                    nodeReplica.group.transform.translateX += nodeReplica.shape.matrix.column.offset * j;
                    nodeReplica.group.transform.translateY += nodeReplica.shape.matrix.row.offset * i;
                    if (nodeReplica.label.sort == 'desc') {
                        nodeReplica.label.text = nodeReplica.label.text.replace("{}", matrix.column.count - j - 1);

                    } else {
                        nodeReplica.label.text = nodeReplica.label.text.replace("{}", j);
                    }

                    nodeReplica.group.id = nodeReplica.group.id.replace("{row}", i).replace("{column}", j);
                    createNode(row, nodeReplica);
                }
                row.addClass("text");
            }
        }
    }

    SVG.on(document, 'DOMContentLoaded', function () {


        ["producer"].forEach(file => {
            $.getJSON(`data/threadpool/${file}.json?${version}`, function (data) {
                data.forEach(node => createNode(svg, node))
            });
        });
        ["workSet","queue","rejectedExecutionHandler","threadCounts"].forEach(file => {
            $.getJSON(`data/threadpool/${file}.json?${version}`, function (node) {
                createMatrix(svg, node);
            });
        });
        $.getJSON("data/threadpool/paths.json?" + version, function (data) {
            data.forEach(value => createPath(svg, value, marker));
        });
        // ["paths-0", "paths-1", "paths-2", "paths-3"].forEach(file => {
        //     $.getJSON(`data/exactly-once/${file}.json?${version}`, function (node) {
        //         node.forEach(path => createSinglePath(svg, path))
        //     });
        // });


        let steps = [
            [show_data_flow.bind(null, ['producer'], [`work-group-1-0-0`],`Thread`,0,0)],
            [show_data_flow.bind(null, ['producer'], [`work-group-1-0-0`],`Thread`,1,1)],
            [show_data_flow.bind(null, ['producer'], [`work-group-1-0-0`],`Thread`,2,2)],
            [hide.bind(null,['data-flow-producer_work-group-1-0-0'])],
            [show_data_flow.bind(null, ['producer'], [`queue-group-1-0-0`],`Task`,0,3)],
            [show_data_flow.bind(null, ['producer'], [`queue-group-1-0-0`],`Task`,1,4)],
            [show_data_flow.bind(null, ['producer'], [`queue-group-1-0-0`],`Task`,2,5)],
            [show_data_flow.bind(null, ['producer'], [`queue-group-1-0-0`],`Task`,3,6)],
            [show_data_flow.bind(null, ['producer'], [`queue-group-1-0-0`],`Task`,4,7)],
            [show_data_flow.bind(null, ['producer'], [`queue-group-1-0-0`],`Task`,5,8)],
            [show_data_flow.bind(null, ['producer'], [`queue-group-1-0-0`],`Task`,6,9)],
            [hide.bind(null,['data-flow-producer_queue-group-1-0-0'])],
            [show_data_flow.bind(null, ['producer'], [`work-group-1-0-1`],`Thread`,0,10,`max`)],
            [show_data_flow.bind(null, ['producer'], [`work-group-1-0-1`],`Thread`,1,11,`max`)],
            [show_data_flow.bind(null, ['producer'], [`work-group-1-0-1`],`Thread`,2,12,`max`)],
            [animate2.bind(null, ['work-group-1-0-1'], [`policy-group-1-0-0`])],
            [hide.bind(null,['data-flow-producer_work-group-1-0-1'])],
            [hide.bind(null,['data-flow-work-group-1-0-1_policy-group-1-0-0'])],
            [take.bind(null,[3])],
            [take.bind(null,[4])],
            [take.bind(null,[5])],
            [take.bind(null,[6])],
            [take.bind(null,[7])],
            [take.bind(null,[8])],
            [take.bind(null,[9])],
            [timeout.bind(null, ['thread'], 2,2)],
            [timeout.bind(null, ['thread'], 1,1)],
            [timeout.bind(null, ['maxThread'], 4,11)],
            [timeout.bind(null, ['maxThread'], 5,12)],
            [timeout.bind(null, ['maxThread'], 3,10)],
            [timeout.bind(null, ['thread'], 0,9)],
        ];
        let stepIndex = 0;

        $("#next").click(function () {
            let step = steps[stepIndex++];
            if(step!=null){
                for (let i = 0; i < step.length; i++) {
                    step[i]();
                }
            }
        });

        $("#image").click(function () {
            if($("#threadStatus").is(":hidden")){
                $("#paper").hide();
                $("#threadStatus").show();
            }else {
                $("#threadStatus").hide();
                $("#paper").show();

            }

        });
        let colors = [
            "#a8e6cf", "#dcedc1", "#ffd3b6", "#ffaaa5",
            "#ff3377", "#ee4035", "#f37736", "#fdf498",
            "#7bc043", "#0392cf", "#8595a1", "#58668b",
        ];

        function timeout(name='thread', threadNum=0,taskNum=0) {

            let x=-600+((threadNum)*60*1.5+60);
            let path = {
                "start": "work-group-1-0-1",
                "end": [
                    "queue-group-1-0-0"
                ],
                "startOffsetX": x,
                "startOffsetY": -60,
                "endOffsetX": 35,
                "endOffsetY": 55,
                "curve": "Q400,150",
                "attr": {
                    "stroke": "blue",
                    "fill": "none"
                }
            }
            createPath(svg,path,marker,threadNum);
            let pathd = `${path.start}_${path.end[0]}${threadNum}`;
            if(threadNum===0){
                 pathd = `${path.start}_${path.end[0]}`;
            }
            let d =$(`#${pathd}`).data('d');
            let move = snap.path(d).attr({id: `pathd-${path.start}_${path.end[0]}${threadNum}`, fill: "none", opacity: "1","marker-end": marker});
            let points = $(`#${pathd}`).data('points');
            let circle = snap.circle(points[0], points[1], 10);
            circle.attr({
                fill: 'green',
                id: `circle-${path.start}_${path.end[0]}-${threadNum}`,
                stroke: "green",
                strokeWidth: 2
            });
            $(`#text-task-${taskNum}`).text('');
            circle.drawAtPath(move, 1300, {callback: function () {
                    let timer=snap.select(`#${name}-path-Thread-${threadNum}`);
                    circle.remove();
                    move.remove();

                    timer.animate({"stroke-dashoffset": 0},
                        6000,  function () {

                            if(threadNum!==0){
                                snap.select(`#${name}-Thread-${threadNum}`).remove();
                                $(`#text-thread-${threadNum}`).text('');
                                timer.attr({"stroke-dashoffset": 315});
                                snap.select(`#${path.start}_${path.end[0]}${threadNum}`).remove();
                                //snap.select(`#pathd-${path.start}_${path.end[0]}-${threadNum}`).remove();
                                //snap.select(`#circle-${path.start}_${path.end[0]}-${threadNum}`).remove();
                                let count = $('#threadCounts-1-0-0').text();
                                $('#threadCounts-1-0-0').text(count-1);
                            }else {
                                snap.select(`#${path.start}_${path.end[0]}`).remove();
                                $(`#text-task-${threadNum}`).text('');
                                timer.attr({"stroke-dashoffset": 315});
                                timeout('thread',0,9);
                            }

                        });
                }});


        }

        function take(count=0) {
            let x=(count-3)*60*1.5;
            animate3(`queue-group-1-0-0`,x, 1, function () {
                // let obj_g = Snap(`#buffer-0-x`).clone();
                let obj_g=snap.select(`#task-Task-${count}`);

                obj_g.animate(
                    {
                        transform: `translate(-${x} 350)`
                    },
                    500, mina.linear);
                snap.select(`#text-task-Task-${count}`).remove();
                sleep(1000).then(()=>{
                    snap.select(`#task-Task-${count}`).remove();
                    $(`#text-task-0`).text(`task-${count}`);
                });

                // svg.findOne(`#text-task-Task-0`).remove();
                // snap.select(`#text-task-Task-0`).text('');

                // snap.select(`#group-task-Task-0`).remove();
            });
        }
        function animate3(pathId, offset=0, loop, callback) {
            let x =-560+offset;
            let path = {
                "start": "queue-group-1-0-0",
                "end": [
                    "work-group-1-0-0"
                ],
                "startOffsetX": x,
                "startOffsetY": -3,
                "endOffsetX": 35,
                "endOffsetY": 20,
                "curve": "Q300,300",
                "attr": {
                    "stroke": "blue",
                    "fill": "none"
                }
            }
            createPath(svg,path,marker,offset);
            let pathd = `${path.start}_${path.end[0]}`;
            if(offset!==0){
                pathd = `${path.start}_${path.end[0]}${offset}`;
            }

            let d =$(`#${pathd}`).data('d');
            let move = snap.path(d).attr({id:`path-${path.start}_${path.end[0]}`,fill: "none", stroke: "red", opacity: "1","marker-end": marker});
            let points = $(`#${pathd}`).data('points');
            let circle = snap.circle(points[0], points[1], 10);
            circle.attr({
                fill: 'green',
                id: `circle-${path.start}_${path.end[0]}`,
                stroke: "green",
                strokeWidth: 2
            });
            circle.drawAtPath(move, 1000, {callback:  function () {
                    callback();
                    snap.select(`#data-flow-${path.start}_${path.end[0]}`).remove();
                    snap.select(`#path-${path.start}_${path.end[0]}`).remove();
                    snap.select(`#circle-${path.start}_${path.end[0]}`).remove();
                }});
        }
        
        function animate2(froms, tos,display = '') {
            for (let i = 0; i < froms.length; i++) {
                for (let j = 0; j < tos.length; j++) {
                    let from = froms[i];
                    let to = tos[j];
                    snap.select(`#${from}`).attr({display: ''});
                    snap.select(`#data-flow-${from}_${to}`).attr({display: display});
                    let move = snap.select(`#${from}_${to}`);
                    let points = $(`#${from}_${to}`).data('points');
                    let circle = snap.circle(points[0], points[1], 10);
                    circle.attr({
                        fill: 'green',
                        id: `circle-${from}_${to}`,
                        stroke: "green",
                        strokeWidth: 2
                    });
                    circle.drawAtPath(move, 1000, {
                        callback: function () {
                            circle.remove();
                            // sleep(100).then(()=>{
                            //     show_data_flow.bind(null, ['producer'], [`work-group-1-0-1`],`Thread`,2,12,`max`)();
                            //     sleep(1000).then(()=>{
                            //         animate2.bind(null, ['work-group-1-0-1'], [`policy-group-1-0-0`])();
                            //     });
                            // });

                        }
                    });
                    // let points = svg.findOne(`#${from}_${to}`).data('points');
                    // // let group = Snap('');
                    // let group =svg.group().id('1111');
                    // console.log(points);
                    // console.log(group);
                    // group.circle(20).attr({cx: points[0], cy: points[1]}).fill('green')
                    //     .id(`circle-${from}_${to}`)
                    //     .animate(2500, 100, 'now')
                    //     .move(points[2] - 10, points[3] - 10)
                    //     .loop(Number.MAX_SAFE_INTEGER, false, Math.random() * 2000)
                }
            }

        }

        function show_data_flow(froms, tos, name='', count=0, task=0, max='',display = '', mapping = 'full') {

            for (let i = 0; i < froms.length; i++) {
                for (let j = 0; j < tos.length; j++) {
                    let from = froms[i];
                    let to = tos[j];
                    snap.select(`#${from}`).attr({display: ''});
                    snap.select(`#${to}`).attr({display: ''});
                    snap.select(`#data-flow-${from}_${to}`).attr({display: display});

                    animate(`${from}_${to}`,function () {

                        let group = snap.select(`#${to}`);
                        let start = count*60*1.5 +60;
                        let end =60;

                        if(max!=null && max !==''){
                            let id = svg.findOne(`#maxThread-${name}-${count}`);
                            if(id!==null){
                                return;
                            }
                            group.circle(start,end,40)
                                .attr({
                                    id: `maxThread-${name}-${count+3}`,
                                    fill: colors[Math.floor(Math.random() * 12)]
                                    // stroke: "gray",
                                    // "stroke-width": 1,
                                })
                                .addClass("dot");
                            group.path(`M ${start}, ${end-40} a 40,40 0 1,1 0,80 a 40,40 0 1,1 0,-80`)
                                .attr({
                                    id: `maxThread-path-${name}-${count+3}`
                                }).addClass("node_timer");
                            group.text(start, end, `${name}-${count+3}`)
                                .attr({id:`text-thread-${count+3}`,'text-anchor': 'middle', "font-size": 20,"font-weight": "bold"});
                            group.text(start, end+20, `task-${task}`)
                                .attr({id:`text-task-${task}`,'text-anchor': 'middle', "font-size": 20});
                            snap.select(`#threadCounts-group-1-0-0`).select("text").remove();
                            snap.select('#threadCounts-group-1-0-0').text(66,68,count+4).attr({id:'threadCounts-1-0-0',fill: "red"});
                        }else if(name==='Task'){
                            let id = svg.findOne(`#task-${name}-${task}`);
                            if(id!==null){
                                return;
                            }
                            group.circle(start,end,40)
                                .attr({
                                    id: `task-${name}-${task}`,
                                    fill: colors[Math.floor(Math.random() * 12)]
                                    // stroke: "gray",
                                    // "stroke-width": 1,
                                })
                                .addClass("dot");
                            group.text(start, end, `${name}-${task}`)
                                .attr({id: `text-task-${name}-${task}`,'text-anchor': 'middle', "font-size": 20,"font-weight": "bold"});
                            // group.text(start, end+20, `task-${task}`)
                            //     .attr({'text-anchor': 'middle', "font-size": 20});
                        }else if(name==='Thread') {
                            let id = svg.findOne(`#thread-${name}-${count}`);
                            if(id!==null){
                                return;
                            }
                            group.circle(start,end,40)
                                .attr({
                                    id: `thread-${name}-${count}`,
                                    fill: colors[Math.floor(Math.random() * 12)]
                                    // stroke: "gray",
                                    // "stroke-width": 1,
                                })
                                .addClass("dot");
                            group.path(`M ${start}, ${end-40} a 40,40 0 1,1 0,80 a 40,40 0 1,1 0,-80`)
                                .attr({
                                    id: `thread-path-${name}-${count}`
                                }).addClass("node_timer");

                            group.text(start, end, `${name}-${count}`)
                                .attr({id:`text-thread-${count}`,'text-anchor': 'middle', "font-size": 20,"font-weight": "bold"});
                            group.text(start, end+20, `task-${task}`)
                                .attr({id:`text-task-${task}`, 'text-anchor': 'middle', "font-size": 20});
                            snap.select(`#threadCounts-group-1-0-0`).select("text").remove();
                            snap.select('#threadCounts-group-1-0-0').text(66,68,count+1).attr({id:'threadCounts-1-0-0',fill: "red"});
                        }


                    });

                    if (mapping = 'one-2-one') break;
                }
            }
        }
        function hideLeaves() {
            snap.selectAll('.leaf').attr({display: 'none'});
        }
        function show() {
            snap.selectAll('.leaf').attr({display: ''});
            snap.selectAll('.path').attr({display: 'none'});
            // snap.select('#partition').attr({display: ''});
            // snap.select('#consumer-group-1').attr({display: ''});
            // snap.select('#offset').attr({display: 'none'});
            // snap.select('#consumer-group-1-0-1').attr({display: 'none'});
        }

        setTimeout(function () {
            hideLeaves();
            show();



            // thanks();
        }, 200);
        $("#threadStatus").hide();
        setInterval(function () {
            let step = steps[stepIndex++];
            if(step!=null){
                for (let i = 0; i < step.length; i++) {
                    step[i]();
                }

            }
        },1200);
    });
    function sleep(wait) {
        return new Promise((res, rej) => {
            setTimeout(() => {
                this.num = 1;
                res(wait);
            }, wait);
        });
    }
    // let s = Snap('#svg');
    // let offset = 50;
    // let g = s.g().attr({ stroke: 'gray' });
    // for (let i = 0; i <=1600 / offset; i++) {
    //     g.line(i * offset, 0, i * offset, 1600)
    //         .attr({"stroke-width": '1', "stroke-dasharray": "5, 5"});
    //     g.line(0, i * offset, 1600, i * offset)
    //         .attr({"stroke-width": '1', "stroke-dasharray": "5, 5"});
    //     g.text(0, i * offset, i * offset);
    //     g.text(i * offset, 20, i * offset);
    // }
</script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"
        integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo"
        crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"
        integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6"
        crossorigin="anonymous"></script>
</body>
</html>